package com.hqd.jjvm.classformat;

import com.hqd.jjvm.classformat.attributeinfo.AttributeInfo;
import com.hqd.jjvm.classformat.attributeinfo.AttributeInfoFactory;
import com.hqd.jjvm.classformat.constantpool.*;
import com.hqd.jjvm.classpath.ClassFileInfo;
import com.hqd.jjvm.util.HexStrTransformUtil;
import lombok.Getter;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 类结构信息
 *
 * @author hqd
 */
@Getter
public class ClassFile extends AbstractClassCode {
    /**
     * 校验器
     */
    private JClassChecker classChecker;

    /**
     * 字节码版本
     */
    private ClassVersion version;
    /**
     * 常量池大小
     */
    private Integer constantPoolCount;
    /**
     * 常量池信息
     */
    private List<ConstantPool> constantPools;
    /**
     * 访问标识
     */
    private Integer accessFlags;
    /**
     * 类名索引
     */
    private Integer thisClassIndex;
    /**
     * 父类索引
     */
    private Integer superClassIndex;
    /**
     * 接口数量
     */
    private Integer interfaceCount;
    /**
     * 接口信息
     */
    private List<ConstantClassInfo> interfaces;
    /**
     * 字段数量
     */
    private Integer fieldCount;
    /**
     * 字段信息
     */
    private List<FieldInfo> fieldInfos;
    /**
     * 方法数量
     */
    private Integer methodCount;
    /**
     * 方法信息
     */
    private List<MethodInfo> methodInfos;
    /**
     * 属性数量
     */
    private Integer attributeCount;
    /**
     * 属性信息
     */
    private List<AttributeInfo> attributeInfos;

    public ClassFile(ClassFileInfo classFileInfo) {
        super(classFileInfo);
        this.classChecker = new JClassChecker(this);
        parseClassFile();
    }


    protected void parseClassFile() {
        parseClassVersion();
        parseConstantPool();
        parseClassAccessFlags();
        parseThisClassIndex();
        parseSuperClassIndex();
        parseInterfaces();
        parseFieldInfo();
        parseMethodInfo();
        parseAttributeInfo();
        classChecker.checkClassFileEnd();
    }


    private void parseAttributeInfo() {
        this.attributeCount = HexStrTransformUtil.parseHexToInt(readU2());
        this.attributeInfos = parseAttributeInfo(attributeCount);
    }

    /**
     * 解析属性列表
     *
     * @param count
     * @return
     */
    public List<AttributeInfo> parseAttributeInfo(int count) {
        List<AttributeInfo> attributeInfos = new LinkedList<>();
        for (int i = 0; i < count; i++) {
            int attributeNameIndex = HexStrTransformUtil.parseHexToInt(read(2, true));
            ConstantUtf8Info constantUtf8Info = (ConstantUtf8Info) constantPools.get(attributeNameIndex);
            String type = constantUtf8Info.getBytes();
            AttributeType attributeType = AttributeType.getTypeByVal(type);
            AttributeInfo attributeInfo = AttributeInfoFactory.newAttributeInfo(this, attributeType);
            attributeInfos.add(attributeInfo);
        }
        return attributeInfos;
    }

    /**
     * 转换方法信息
     */
    private void parseMethodInfo() {
        this.methodCount = HexStrTransformUtil.parseHexToInt(readU2());
        this.methodInfos = new ArrayList<>(methodCount);
        for (int i = 0; i < this.methodCount; i++) {
            MethodInfo methodInfo = new MethodInfo();
            methodInfo.setAccessFlags(HexStrTransformUtil.parseHexToInt(readU2()));
            methodInfo.setNameIndex(HexStrTransformUtil.parseHexToInt(readU2()));
            methodInfo.setDescriptorIndex(HexStrTransformUtil.parseHexToInt(readU2()));
            int attributeCount = HexStrTransformUtil.parseHexToInt(readU2());
            methodInfo.setAttributeCount(attributeCount);
            methodInfo.setAttributeInfos(parseAttributeInfo(attributeCount));
            this.methodInfos.add(methodInfo);
        }
    }

    /**
     * 转换字段信息
     */
    private void parseFieldInfo() {
        this.fieldCount = HexStrTransformUtil.parseHexToInt(readU2());
        this.fieldInfos = new ArrayList<>(fieldCount);
        for (int i = 0; i < this.fieldCount; i++) {
            FieldInfo fieldInfo = new FieldInfo();
            fieldInfo.setAccessFlags(HexStrTransformUtil.parseHexToInt(readU2()));
            fieldInfo.setNameIndex(HexStrTransformUtil.parseHexToInt(readU2()));
            fieldInfo.setDescriptorIndex(HexStrTransformUtil.parseHexToInt(readU2()));
            Integer attrCount = HexStrTransformUtil.parseHexToInt(readU2());
            fieldInfo.setAttributeCount(attrCount);
            fieldInfo.setAttributeInfos(parseAttributeInfo(attrCount));
            this.fieldInfos.add(fieldInfo);
        }
    }

    /**
     * 读取实现接口信息
     */
    private void parseInterfaces() {
        this.interfaceCount = HexStrTransformUtil.parseHexToInt(readU2());
        this.interfaces = new ArrayList<>(interfaceCount);
        for (int i = 0; i < interfaceCount; i++) {
            Integer index = HexStrTransformUtil.parseHexToInt(readU2());
            /**
             * 添加接口信息到集合中
             */
            this.interfaces.add((ConstantClassInfo) this.constantPools.get(index));
        }
    }

    /**
     * 读取父类名
     */
    private void parseSuperClassIndex() {
        this.superClassIndex = HexStrTransformUtil.parseHexToInt(readU2());
    }

    /**
     * 读取类名
     */
    private void parseThisClassIndex() {
        this.thisClassIndex = HexStrTransformUtil.parseHexToInt(readU2());
    }

    /**
     * 读取类标识
     */
    private void parseClassAccessFlags() {
        this.accessFlags = HexStrTransformUtil.parseHexToInt(readU2());
    }

    /**
     * 读取常量池
     */
    private void parseConstantPool() {
        /**
         * 第一个是空，和常量池对应
         */
        this.constantPoolCount = HexStrTransformUtil.parseHexToInt(readU2());
        this.constantPools = new ArrayList<>(constantPoolCount);
        this.constantPools.add(new ConstantPool(""));
        for (int i = 1; i < constantPoolCount; ) {
            ConstantInfoType tag = ConstantInfoType.getType(Integer.parseUnsignedInt(readU1(), 16));
            List<ConstantPool> constantPoolList = ConstantPoolFactory.newConstantPool(tag, this);
            this.constantPools.addAll(constantPoolList);
            i += constantPoolList.size();
        }
    }

    /**
     * 读取魔数和版本号
     */
    private void parseClassVersion() {
        //魔数
        String magic = readU4();
        classChecker.checkMagicNumber(magic);
        //minor_version 副版本号
        float minorVersion = HexStrTransformUtil.parseHexToFloat(readU2());
        //major_version 主版本号
        float majorVersion = HexStrTransformUtil.parseHexToFloat(readU2());
        classChecker.checkClassVersion(majorVersion, minorVersion);
        this.version = new ClassVersion(magic, minorVersion, majorVersion);
    }
}
